From 5788338a14b75c5adc38e85392abced2e6acd993 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 7 Mar 2021 10:02:46 -0500 Subject: [PATCH] ngl: Classify clips Maintain the 'fully contained' information in the clip stack. This will let us pick simpler shaders in the future. --- gsk/ngl/gsknglrenderjob.c | 139 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/gsk/ngl/gsknglrenderjob.c b/gsk/ngl/gsknglrenderjob.c index f2dec8d9e8..41608993fb 100644 --- a/gsk/ngl/gsknglrenderjob.c +++ b/gsk/ngl/gsknglrenderjob.c @@ -80,6 +80,7 @@ typedef struct _GskNglRenderClip { GskRoundedRect rect; guint is_rectilinear : 1; + guint is_fully_contained : 1; } GskNglRenderClip; typedef struct _GskNglRenderModelview @@ -583,6 +584,32 @@ gsk_ngl_render_job_push_clip (GskNglRenderJob *job, clip = &g_array_index (job->clip, GskNglRenderClip, job->clip->len - 1); memcpy (&clip->rect, rect, sizeof *rect); clip->is_rectilinear = gsk_rounded_rect_is_rectilinear (rect); + clip->is_fully_contained = FALSE; + + job->current_clip = clip; +} + +static void +gsk_ngl_render_job_push_contained_clip (GskNglRenderJob *job) +{ + GskNglRenderClip *clip; + GskNglRenderClip *old_clip; + + g_assert (job != NULL); + g_assert (job->clip != NULL); + g_assert (job->clip->len > 0); + + job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++; + + old_clip = &g_array_index (job->clip, GskNglRenderClip, job->clip->len - 1); + + g_array_set_size (job->clip, job->clip->len + 1); + + clip = &g_array_index (job->clip, GskNglRenderClip, job->clip->len - 1); + memcpy (&clip->rect.bounds, &old_clip->rect.bounds, sizeof (graphene_rect_t)); + memset (clip->rect.corner, 0, sizeof clip->rect.corner); + clip->is_rectilinear = TRUE; + clip->is_fully_contained = TRUE; job->current_clip = clip; } @@ -742,13 +769,108 @@ gsk_ngl_render_job_transform_rounded_rect (GskNglRenderJob *job, memcpy (out_rect->corner, rect->corner, sizeof rect->corner); } +static inline void +rounded_rect_get_inner (const GskRoundedRect *rect, + graphene_rect_t *inner) +{ + float left = MAX (rect->corner[GSK_CORNER_TOP_LEFT].width, rect->corner[GSK_CORNER_BOTTOM_LEFT].width); + float right = MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width, rect->corner[GSK_CORNER_BOTTOM_RIGHT].width); + float top = MAX (rect->corner[GSK_CORNER_TOP_LEFT].height, rect->corner[GSK_CORNER_TOP_RIGHT].height); + float bottom = MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height, rect->corner[GSK_CORNER_BOTTOM_RIGHT].height); + + inner->origin.x = rect->bounds.origin.x + left; + inner->size.width = rect->bounds.size.width - (left + right); + + inner->origin.y = rect->bounds.origin.y + top; + inner->size.height = rect->bounds.size.height - (top + bottom); +} + static inline gboolean -gsk_ngl_render_job_node_overlaps_clip (GskNglRenderJob *job, - const GskRenderNode *node) +interval_contains (float p1, float w1, + float p2, float w2) +{ + if (p2 < p1) + return FALSE; + + if (p2 + w2 > p1 + w1) + return FALSE; + + return TRUE; +} + +static inline gboolean +gsk_ngl_render_job_update_clip (GskNglRenderJob *job, + const GskRenderNode *node, + gboolean *pushed_clip) { graphene_rect_t transformed_bounds; + gboolean no_clip = FALSE; + gboolean rect_clip = FALSE; + + *pushed_clip = FALSE; + + if (job->current_clip->is_fully_contained) + { + /* Already fully contained - no further checks needed */ + return TRUE; + } + gsk_ngl_render_job_transform_bounds (job, &node->bounds, &transformed_bounds); - return rect_intersects (&job->current_clip->rect.bounds, &transformed_bounds); + + if (!rect_intersects (&job->current_clip->rect.bounds, &transformed_bounds)) + { + /* Completely clipped away */ + return FALSE; + } + + if (job->current_clip->is_rectilinear) + { + if (rect_contains_rect (&job->current_clip->rect.bounds, &transformed_bounds)) + no_clip = TRUE; + else + rect_clip = TRUE; + } + else if (gsk_rounded_rect_contains_rect (&job->current_clip->rect, &transformed_bounds)) + { + no_clip = TRUE; + } + else + { + graphene_rect_t inner; + + rounded_rect_get_inner (&job->current_clip->rect, &inner); + + if (interval_contains (inner.origin.x, inner.size.width, + transformed_bounds.origin.x, transformed_bounds.size.width) || + interval_contains (inner.origin.y, inner.size.height, + transformed_bounds.origin.y, transformed_bounds.size.height)) + rect_clip = TRUE; + } + + if (no_clip) + { + /* This node is completely contained inside the clip. + * Record this fact on the clip stack, so we don't do more work + * for child nodes. + */ + + gsk_ngl_render_job_push_contained_clip (job); + + *pushed_clip = TRUE; + } + else if (rect_clip && !job->current_clip->is_rectilinear) + { + graphene_rect_t rect; + + /* The clip gets simpler for this node */ + + graphene_rect_intersection (&job->current_clip->rect.bounds, &transformed_bounds, &rect); + gsk_ngl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (rect)); + + *pushed_clip = TRUE; + } + + return TRUE; } /* load_vertex_data_with_region */ @@ -3243,13 +3365,17 @@ static void gsk_ngl_render_job_visit_node (GskNglRenderJob *job, const GskRenderNode *node) { + gboolean has_clip; + g_assert (job != NULL); g_assert (node != NULL); g_assert (GSK_IS_NGL_DRIVER (job->driver)); g_assert (GSK_IS_NGL_COMMAND_QUEUE (job->command_queue)); - if (node_is_invisible (node) || - !gsk_ngl_render_job_node_overlaps_clip (job, node)) + if (node_is_invisible (node)) + return; + + if (!gsk_ngl_render_job_update_clip (job, node, &has_clip)) return; switch (gsk_render_node_get_node_type (node)) @@ -3397,6 +3523,9 @@ gsk_ngl_render_job_visit_node (GskNglRenderJob *job, g_assert_not_reached (); break; } + + if (has_clip) + gsk_ngl_render_job_pop_clip (job); } static gboolean -- 2.30.2